package com.boss.salary.service.impl;

import com.boss.common.entity.PageDTO;
import com.boss.common.service.ICommonService;
import com.boss.framework.dto.PaginationDTO;
import com.boss.salary.constant.SystemDictConstant;
import com.boss.salary.dao.IAgencySalaryDao;
import com.boss.salary.dao.IFormulaManageDao;
import com.boss.salary.dao.ISalaryBasedataDao;
import com.boss.salary.dao.ISalaryMonthEndDao;
import com.boss.salary.entity.FormulaManage;
import com.boss.salary.entity.SalaryBasedata;
import com.boss.salary.service.IFormulaManageService;
import com.boss.salary.vo.AgencySalaryVO;
import com.boss.salary.vo.SalaryMonthEndVO;
import com.boss.sys.ca.user.dto.UserDTO;
import com.singularsys.jep.Jep;
import com.singularsys.jep.JepException;
import com.singularsys.jep.bigdecimal.BigDecComponents;
import com.singularsys.jep.bigdecimal.functions.BigDecRound;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.math.MathContext;
import java.util.*;
import java.util.stream.Collectors;

@Service
public class FormulaManageServiceImpl implements IFormulaManageService {

    @Autowired
    private IFormulaManageService formulaManageService;

    @Autowired
    private IFormulaManageDao formulaManageDao;
    @Autowired
    private IAgencySalaryDao agencySalaryDao;
    @Autowired
    private ISalaryBasedataDao salaryBasedataDao;
    @Autowired
    private ISalaryMonthEndDao salaryMonthEndDao;
    @Autowired
    private ICommonService iCommonService;


    @Override
    public PageDTO<FormulaManage> getFormulaManagePage(PaginationDTO paginationDTO, String agencyId, Map<String, Object> queryForm) {
        List<FormulaManage> result = formulaManageDao.selectFormulaManagePage(paginationDTO, agencyId, queryForm);

        queryForm.put("type","salary_type");
        List<Map<String, Object>> personTypeList=iCommonService.queryBaseDataList(queryForm);
        if(!CollectionUtils.isEmpty(personTypeList)) {
            result.forEach(r -> {
                if (!StringUtils.isEmpty(r.getMeritPayCode())) {
                    List<String> list = Arrays.asList(r.getMeritPayCode().split(","));
                    String meritPayCode = "";
                    int a=0;
                    for (int i = 0; i < list.size(); i++) {
                        for (Map<String, Object> map : personTypeList) {
                            if (list.get(i).equals(map.get("code"))) {
                                if(a!=0) {
                                    meritPayCode += ",";
                                }
                                meritPayCode += map.get("name");
                                a++;
                                continue;

                            }

                        }
//                    switch (list.get(i)){
//                        case "0" : meritPayCode += ",行政人员"; break;
//                        case "1" : meritPayCode += ",事业人员"; break;
//                        case "11" : meritPayCode += ",离休人员"; break;
//                    }
                    }
                    r.setMeritPayCode(meritPayCode);
                }
            });
        }
        int total = formulaManageDao.selectFormulaManageTotal(agencyId, queryForm);
        PageDTO<FormulaManage> pageDTO = new PageDTO<>();
        pageDTO.setRows(result);
        pageDTO.setTotal(total);
        return pageDTO;
    }

    @Override
    public List<FormulaManage> generateFormula(UserDTO userDTO,String agencyId) {
        List<AgencySalaryVO> agencySalaryList = agencySalaryDao.selectColumnName(userDTO);
        Map<String, String> salaryIdJoinColumnNameMap = agencySalaryList.stream()
                .collect(Collectors.toMap(AgencySalaryVO::getSalaryId, AgencySalaryVO::getColumnName));
        //获取计算值得工资id
        int isCompute = 1;
        List<String> needComputeSalaryIds = agencySalaryList.stream()
                .filter(agencySalaryVO -> agencySalaryVO.getIsCompute().equals(isCompute))
                .map(AgencySalaryVO::getSalaryId)
                .collect(Collectors.toList());
        if (CollectionUtils.isEmpty(needComputeSalaryIds)) {
            return Collections.emptyList();
        }
        List<FormulaManage> formulaManageList = formulaManageDao.selectFormulaBySalaryId(agencyId, needComputeSalaryIds);
        return formulaManageList.stream()
                .map(formulaManage -> replaceArgumentFromFormula(salaryIdJoinColumnNameMap, formulaManage))
                .collect(Collectors.toList());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void submitFormula(FormulaManage formulaManage) throws NoSuchMethodException, JepException, IllegalAccessException, InvocationTargetException {
        if (!CollectionUtils.isEmpty(formulaManage.getMeritPayCodeList())){
            formulaManage.setMeritPayCode(String.join(",", formulaManage.getMeritPayCodeList()));
        }
        if (StringUtils.isEmpty(formulaManage.getId())) {
//          新增：通过工资项id查询是否有数据，有则将之前的公式停用再新增，没有则直接新增
            formulaManage.setIsDeleted(0);
            formulaManage.setOpenType("1");
            formulaManageDao.updateOpenTypeBySalaryId(formulaManage.getAgencyId(), formulaManage.getSalaryId(), SystemDictConstant.DISABLE,formulaManage.getMeritPayCode());
            formulaManageDao.insertFormulaManage(formulaManage);
        } else {
//          编辑:
            formulaManageDao.updateFormulaManage(formulaManage);
        }
        String province = formulaManage.getMofDivCode();
        List<String> agencyIds = Collections.singletonList(formulaManage.getAgencyId());
//        查询需要操作的人员工资
        List<SalaryMonthEndVO> salaryMonthEndVOList = salaryMonthEndDao.selectSalaryMonthEndWithSalaryType(province, agencyIds);
//        计算工资
        salaryMonthEndVOList = formulaManageService.calculateByFormula(province, agencyIds, salaryMonthEndVOList);
//        保存修改后工资
        salaryMonthEndDao.batchUpdateSalaryMonthEnd(salaryMonthEndVOList);
    }

    @Override
    public FormulaManage getFormulaDetail(String id) {
        FormulaManage formulaManage = formulaManageDao.selectFormulaDetail(id);
        if (!StringUtils.isEmpty(formulaManage.getMeritPayCode())) {
            String[] meritPayCode = formulaManage.getMeritPayCode().split(",");
            formulaManage.setMeritPayCodeList(Arrays.asList(meritPayCode));
        }
        return formulaManage;
    }

    @Override
    public List<SalaryBasedata> getAllSalary(String province) {
        return salaryBasedataDao.selectAllSalary(province);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void removeFormulaById(String id) {
        formulaManageDao.deleteFormulaById(id);
    }

    @Override
    public List<SalaryMonthEndVO> calculateByFormula(String province, List<String> agencyIds, List<SalaryMonthEndVO> salaryMonthEndVOList) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException, JepException {
//        获取当前区划下所有的工资项
        List<SalaryBasedata> allSalaryAndColumn = salaryBasedataDao.selectAllSalaryAndColumn(province);
        Map<String, String> salaryJoinCode = allSalaryAndColumn.stream().collect(Collectors.toMap(SalaryBasedata::getId, SalaryBasedata::getSalaryCode));
//        获取所有需要计算的工资项
        Integer isCompute = 1;
        List<SalaryBasedata> computeSalary = allSalaryAndColumn.stream().filter(salary -> isCompute.equals(salary.getIsCompute())).collect(Collectors.toList());
       //保存工资项小数点保留位数用key:salary_code  value:decimalPlace
        Map<String,Integer> decimalPlaceMap =new HashMap<>();
//        如果没有需要计算的工资项，则直接返回
        if (!CollectionUtils.isEmpty(computeSalary)) {
            for(SalaryBasedata sb:computeSalary){
                decimalPlaceMap.put(sb.getSalaryCode(),sb.getDecimalPlace());
            }
//            查询当前区划下所有的公式
            List<String> computeSalaryIds = computeSalary.stream().map(SalaryBasedata::getId).collect(Collectors.toList());
            List<FormulaManage> formulaManageList = formulaManageDao.selectFormulaBySalaryId(province, agencyIds, computeSalaryIds);
            //若未查到公式，则直接返回
            if(!CollectionUtils.isEmpty(formulaManageList)){
            for (FormulaManage formulaManage : formulaManageList) {
                String computeColumn = salaryJoinCode.get(formulaManage.getSalaryId());
//                保存计算工资项字段
                formulaManage.setComputeColumn(computeColumn);
                StringBuilder sb = new StringBuilder();
                String[] formulas = formulaManage.getFormula().split(",");
                List<String> columns = new ArrayList<>();
                for (String formulaArg : formulas) {
                    if (formulaArg.startsWith("&") && formulaArg.endsWith("&")) {
                        String salaryId = formulaArg.substring(1, formulaArg.length() - 1);
                        sb.append(salaryJoinCode.get(salaryId));
                        columns.add(salaryJoinCode.get(salaryId));
                    } else {
                        sb.append(formulaArg);
                    }
                }
//                保存计算公式中的工资项字段
                formulaManage.setColumns(columns);
//                保存计算公式
                formulaManage.setComputeFormula(sb.toString());
            }
//            机构关联公式map
            Map<String, List<FormulaManage>> agencyIdJoinFormula = formulaManageList.stream().collect(Collectors.groupingBy(FormulaManage::getAgencyId));
            Class<?> clazz = SalaryMonthEndVO.class;

                for (SalaryMonthEndVO salaryMonthEndVO : salaryMonthEndVOList) {
                    String agencyId = salaryMonthEndVO.getAgencyId();
                    List<FormulaManage> formulaList = agencyIdJoinFormula.get(agencyId);
                    if(CollectionUtils.isEmpty(formulaList)){
                        continue;
                    }
                    for (FormulaManage formulaManage : formulaList) {
    //                    判断工资类别
                        String meritPayCode = formulaManage.getMeritPayCode();
                        List<String> meritPayCodeList = new ArrayList<>(Arrays.asList(meritPayCode.split(",")));
                        if(meritPayCodeList.contains(salaryMonthEndVO.getMeritPayCode())){
    //                    引入公式计算类
                            computeSalary(clazz, salaryMonthEndVO, formulaManage,decimalPlaceMap);
                        }
                    }
                }
            }
        }
        return salaryMonthEndVOList;
    }

    private void computeSalary(Class<?> clazz, SalaryMonthEndVO salaryMonthEndVO, FormulaManage formulaManage,Map<String,Integer> decimalPlaceMap) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, JepException {
        Jep jep = new Jep(new BigDecComponents(MathContext.DECIMAL64, Boolean.TRUE));
        BigDecRound bigDecRound = new BigDecRound();
        List<String> columns = formulaManage.getColumns();
        columns.remove(null);
        for (String column : columns) {
            if(StringUtils.isEmpty(column)){
                continue;
            }
            Method getMethod = clazz.getMethod("get" + column.substring(0, 1).toUpperCase() + column.substring(1));
            Object value = getMethod.invoke(salaryMonthEndVO);
            jep.addVariable(column, StringUtils.isEmpty(value) ? 0 : value);
        }
        //获取公式，若公式中存在null值则转化成0
        String computeFormula = formulaManage.getComputeFormula().replace("null","0");
        jep.parse(computeFormula);
        String computeColumn = formulaManage.getComputeColumn();
        Double resultValue = Double.parseDouble(bigDecRound.round(jep.evaluate(), decimalPlaceMap.get(computeColumn)).toString());

        Method setMethod = clazz.getMethod("set" + computeColumn.substring(0, 1).toUpperCase() + computeColumn.substring(1), Double.class);
        setMethod.invoke(salaryMonthEndVO, resultValue);
    }

    private FormulaManage replaceArgumentFromFormula(Map<String, String> salaryIdJoinColumnNameMap, FormulaManage formulaManage) {
        StringBuilder sb = new StringBuilder();
//        等号左边
        sb.append(salaryIdJoinColumnNameMap.get(formulaManage.getSalaryId())).append("=");
//        等号右边
        List<String> formulaArgs = new ArrayList<>(Arrays.asList(formulaManage.getFormula().split(",")));
        for (String formulaArg : formulaArgs) {
            if (formulaArg.startsWith("&") && formulaArg.endsWith("&")) {
                //说明当前为工资项
                String salaryId = formulaArg.substring(1, formulaArg.length() - 1);
                sb.append("&").append(salaryIdJoinColumnNameMap.get(salaryId)).append("&");
            } else {
                //说明为计算符号和数字
                sb.append(formulaArg);
            }
            if (!(formulaArgs.indexOf(formulaArg) == formulaArgs.size() - 1)) {
                sb.append(",");
            }
        }
        formulaManage.setComputeFormula(sb.toString());
        return formulaManage;
    }

    /**
     * 复制方式方法
     * @param formulaManage
     * @param province
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void saveCopyFormula(FormulaManage formulaManage,String province) {
        // 通过单位id 和区划
        List<FormulaManage> result = formulaManageDao.selectFormulaManage(formulaManage.getAgencyId(),province);
        // 删除 要复制单位的 公式
        formulaManageDao.deleteFormula(formulaManage.getAgencyIds());
        List<FormulaManage> formulaManageList=new ArrayList<>();
        for(String agencyid:formulaManage.getAgencyIds()){
            for(FormulaManage formulaManages:result){
                FormulaManage formula =new FormulaManage();
                BeanUtils.copyProperties(formulaManages,formula);
                formula.setAgencyId(agencyid);
                formulaManageList.add(formula);
            }
        }

        /**
         * 保存公式
         */
        formulaManageDao.insertFormula(formulaManageList);
    }

}
